package com.streamtest;

import org.apache.commons.lang3.StringUtils;

import java.util.*;
import java.util.function.Function;
import java.util.stream.Collectors;
import java.util.stream.Stream;

import static java.util.stream.Collectors.toList;

/**
 * @author lishuai
 * @email lishuai@uniontech.com
 * @date 2021/3/4 19:55
 * @description java 8 stream
 */
public class Java8Stream {

    /*
        1.什么是Stream?
         * Java 8 中的 Stream 是对集合（Collection）对象功能的增强，它专注于对集合对象进行各种非常便利、
            高效的聚合操作（aggregate operation），或者大批量数据操作 (bulk data operation)。
         * Stream API 借助于同样新出现的 Lambda 表达式，极大的提高编程效率和程序可读性。
            同时它提供串行和并行两种模式进行汇聚操作，并发模式能够充分利用多核处理器的优势

        2.为什么要用Stream?
         * 有高效率的并行操作（stream串行,parallelStream并行）
         * 有多中功能性的聚合操作count（）、sum（）、avg（）....
         * 函数式编程，使代码更加简洁，提高编程效率

        3.怎么使用stream?
         * Stream的操作可以分为两大类：中间操作、终结操作
            + 中间操作:[筛选(filter(),limit(n),skip(n),distinct),映射(map()),排序(sorted())]
                - 无状态（Stateless）操作：指元素的处理不受之前元素的影响[filter()、map()、mapToInt()、mapToLong()、mapToDouble() ....]
                - 有状态（Stateful）操作：指该操作只有拿到所有元素之后才能继续下去[distinct()、sorted()、limit()、skip()....]
            + 终结操作:[聚合(max()、min()、count()),收集操作:将流中元素收集成另外一个数据结构]
                - 短路（Short-circuiting）操作：指遇到某些符合条件的元素就可以得到最终结果[forEach()、forEachOrdered()、max()、min()、count()]
                - 非短路（Unshort-circuiting）操作：指必须处理完所有元素才能得到最终结果[findFirst()]
         * 当我们使用一个流的时候，通常包括三个基本步骤：
            + 获取一个数据源[.stream/.parallelStream] → 数据转换[一般为中间操作][可多次] → 执行操作获取想要的结果[一般为终结操作][可以没有该操作]

        4.stream和parallelStream的简单区分
            * stream是顺序流，由主线程按顺序对流执行操作，而parallelStream是并行流，内部以多线程并行执行的方式对流进行操作，
                需要注意使用并行流的前提是流中的数据处理没有顺序要求（会乱序，即使用了forEachOrdered）

        5.API操作案例
    */
    public static void main(String[] args) {
//        test();
//        test2();
//        test3();
//    List<Integer> list = Arrays.asList(6, 7, 3, 8, 1, 2);
//        //1.forEach: 迭代循环
//    list.stream().forEach(System.out::println);
//    getUserList().stream().forEach(userInfo -> {
//        System.out.println(userInfo.toString());
//    });

        //2. filter: 筛选，是按照一定的规则校验流中的元素，将符合条件的元素提取到新的流中的操作。[过滤满足条件的保留下来]
        // 简单操作
//    list.stream().filter(element -> element > 5).forEach(System.out::println);
//    System.out.println(list.toString());
//    // 对于对象集合的 操作
//    getUserList().stream().forEach(System.out::println);
//    getUserList().stream().filter(userInfo -> userInfo.getAge() > 20).forEach(System.out::println);
//    getUserList().stream().filter(userInfo -> userInfo.getAge() > 20 && userInfo.getAge() < 23).collect(Collectors.toList());
//
//    //3.map: 接收一个函数作为参数，该函数会被应用到每个元素上，并将其映射成一个新的元素。
//    getUserList().stream().map(userInfo -> {
//        userInfo.setAge(userInfo.getAge() + 1);
//        return userInfo;
//    }).forEach(System.out::println);
//
//    getUserList().stream().map(UserInfo::getId).forEach(System.out::println);
//
//
//    //4.flatMap: 接收一个函数作为参数，将流中的每个值都换成另一个流，然后把所有流连接成一个流。
//    List<String> flatList = Arrays.asList("a,b,c", "1,2,3");
//    //将每个元素转成一个新的且不带逗号的元素
//    flatList.stream().map(s -> s.replaceAll(",", "")).forEach(System.out::println);
//    flatList.stream().flatMap(s -> {
//        //将每个元素转换成一个stream
//        String[] split = s.split(",");
//        return Arrays.stream(split);
//    }).forEach(System.out::println);
//
//    //5.sorted: 返回由该流的元素组成的流，并根据自然顺序排序
//    getUserList().stream().sorted(Comparator.comparing(UserInfo::getAge).reversed()).forEach(System.out::println);
//
//    //6.收集操作->转换成Map
//    Map<Integer, UserInfo> map = getUserList().stream().collect(Collectors.toMap(UserInfo::getId, Function.identity()));
//    System.out.println(map.toString());
//
//    Map<Integer, UserInfo> map2 = getUserList().stream().collect(Collectors.toMap(UserInfo::getId, a -> a, (k1, k2) -> k1));
//    System.out.println(map2.toString());
//
//    Map<Integer, String> map3 = getUserList().stream().collect(Collectors.toMap(UserInfo::getId, UserInfo::getName, (k1, k2) -> k1));
//    System.out.println(map3.toString());
//
//    //7.对象去重
//    getUserList().stream().collect(Collectors.collectingAndThen(Collectors.toCollection(
//            () -> new TreeSet<>(Comparator.comparing(UserInfo::getName))), ArrayList::new)).forEach(System.out::println);

        // forEachAndFind ();

        // fitterList();

//         maxOrMinAndCount();

//        mapAndFlatMap ();

         streamSort();

//        streamDistinct ();

        // streamCollector ();

        //streamCount ();

         partitioningByAndGroupingBy ();

        //streamJoining();

//        peekAndMapTest();

    }

    public static void forEachAndFind() {
        List<Integer> list = Arrays.asList(7, 6, 9, 3, 8, 2, 1);

        // 遍历输出符合条件的元素
        list.stream().filter(x -> x > 6).forEach(System.out::println);
        // 798

        // 匹配第一个
        Optional<Integer> findFirst = list.stream().filter(x -> x > 6).findFirst();
        System.out.println("匹配第一个值：" + findFirst.get());
        // 7

        // 匹配任意（适用于并行流）
        Optional<Integer> findAny = list.parallelStream().filter(x -> x > 6).findAny();
        System.out.println("匹配任意一个值：" + findAny.get());
        // 7 / 9 /8

        // 是否包含符合特定条件的元素
        boolean anyMatch = list.stream().anyMatch(x -> x < 6);
        System.out.println("是否存在大于6的值：" + anyMatch);
        // true

    }

    public static void fitterList() {
//        List<Person> personList = new ArrayList<>();
//        personList.add(new Person("Tom", 8900, 23, "male", "New York"));
//        personList.add(new Person("Jack", 7000, 25, "male", "Washington"));
//        personList.add(new Person("Lily", 7800, 21, "female", "Washington"));
//        personList.add(new Person("Anni", 8200, 24, "female", "New York"));
//        personList.add(new Person("Owen", 9500, 25, "male", "New York"));
//        personList.add(new Person("Alisa", 7900, 26, "female", "New York"));
//
//        List<String> fitterList = personList.stream().filter(x -> x.getSalary() > 8000).map(Person::getName)
//                .collect(Collectors.toList());
//        System.out.print("高于8000的员工姓名：" + fitterList);
        // 高于8000的员工姓名：[Tom, Anni, Owen]
    }

    public static void maxOrMinAndCount() {

//        List<Person> personList = new ArrayList<>();
//        personList.add(new Person("Tom", 8900, 23, "male", "New York"));
//        personList.add(new Person("Jack", 7000, 25, "male", "Washington"));
//        personList.add(new Person("Lily", 7800, 21, "female", "Washington"));
//        personList.add(new Person("Anni", 8200, 24, "female", "New York"));
//        personList.add(new Person("Owen", 9500, 25, "male", "New York"));
//        personList.add(new Person("Alisa", 7900, 26, "female", "New York"));
//
//        Optional<Person> max = personList.stream().max(Comparator.comparing(Person::getSalary));
//        System.out.println("员工工资最大值：" + max.get().getSalary());
//        // 员工工资最大值：9500
//
//        long count = personList.stream().filter(x -> x.getSalary() > 8000).count();
//        System.out.println("员工工资大于8000人数：" + count);
//        // 员工工资大于8000人数：3
    }

    public static void mapAndFlatMap() {

//        List<Person> personList = new ArrayList<Person>();
//        personList.add(new Person("Tom", 8900, 23, "male", "New York"));
//        personList.add(new Person("Jack", 7000, 25, "male", "Washington"));
//        personList.add(new Person("Lily", 7800, 21, "female", "Washington"));
//        personList.add(new Person("Anni", 8200, 24, "female", "New York"));
//        personList.add(new Person("Owen", 9500, 25, "male", "New York"));
//        personList.add(new Person("Alisa", 7900, 26, "female", "New York"));
//
//        List<Person> personListNew = personList.stream().filter(person -> person.getAge() > 25)
//                .map(person -> {
//                    person.setSalary(person.getSalary() + 10000);
//                    return person;
//        }).collect(Collectors.toList());
//        System.out.println("超过25岁员工工资加一万以后：" +personListNew.get(0).getSalary());
//        // 超过25岁员工工资加一万以后：17900

//        List<String> list = Arrays.asList("m,k,l,a", "1,3,5,7");
//        List<String> listNew = list.stream().flatMap(s -> {
//            // 将每个元素转换成一个stream
//            String[] split = s.split(",");
//            Stream<String> s2 = Arrays.stream(split);
//            return s2;
//        }).collect(Collectors.toList());
//        System.out.println("处理后的集合：" + listNew);
//        // [m, k, l, a, 1, 3, 5]
//
//
//        List<Integer> numbers1 = Arrays.asList(1, 2, 3);
//        List<Integer> numbers2 = Arrays.asList(3, 4);
//        numbers1.stream().flatMap(
//                a -> numbers2.stream().map(b -> new int[]{a, b})
//        ).collect(Collectors.toList()).forEach(c -> System.out.println(Arrays.toString(c)));

        String[] words = new String[]{"Hello","World"};
        List<String[]> a = Arrays.stream(words)
                .map(word -> word.split(""))
                .distinct()
                .collect(Collectors.toList());
        a.forEach(System.out::print);

    }

    public static void streamSort() {

        List<Person> personList = new ArrayList<>();
        personList.add(new Person("Sherry", 9000, 24, "female", "New York"));
        personList.add(new Person("Tom", 8900, 22, "male", "Washington"));
        personList.add(new Person("Tom2", 8900, 23, "male", "Washington"));
        personList.add(new Person("Jack", 9000, 25, "male", "Washington"));
        personList.add(new Person("Lily", 8800, 26, "male", "New York"));
        personList.add(new Person("Alisa", 9000, 26, "female", "New York"));

        // 按工资升序排序（自然排序）
        List<String> newListAsc = personList.stream().sorted(Comparator.comparing(Person::getSalary)).map(Person::getName)
                .collect(Collectors.toList());
        System.out.println("按工资升序排序：" + newListAsc);
        // 按工资自然排序：[Lily, Tom, Sherry, Jack, Alisa]

        // 按工资倒序排序
        List<String> newListDesc = personList.stream().sorted(Comparator.comparing(Person::getSalary).reversed())
                .map(Person::getName).collect(Collectors.toList());
        System.out.println("按工资降序排序：" + newListDesc);
        // 按工资降序排序：[Sherry, Jack, Alisa,Tom, Lily]

        // 先按工资再按年龄升序排序
        List<String> newListOrder = personList.stream()
                .sorted(Comparator.comparing(Person::getSalary, Comparator.reverseOrder()).thenComparing(Person::getAge, Comparator.reverseOrder())).map(Person::getName)
                .collect(Collectors.toList());
        System.out.println("先按工资再按年龄升序排序：" + newListOrder);
        // 先按工资再按年龄自然排序：[Sherry, Jack, Alisa, Tom, Lily]

        // 先按工资再按年龄自定义排序（降序）
        List<String> newList = personList.stream().sorted((p1, p2) -> {
            if (p1.getSalary() == p2.getSalary()) {
                return p2.getAge() - p1.getAge();
            } else {
                return p2.getSalary() - p1.getSalary();
            }
        }).map(Person::getName).collect(Collectors.toList());
        System.out.println("先按工资再按年龄自定义降序排序：" + newList);
        // 先按工资再按年龄自定义降序排序：[Alisa, Jack, Sherry, Tom, Lily]

//        List<Person> people1 = new ArrayList<>();
//        people1.add(new Person("Bob", "是"));
//        people1.add(new Person("", "否"));
//        people1.add(new Person("Alice", "是"));
//        people1.add(new Person("", "否"));
//        people1.add(new Person("", "是"));
//        people1.add(new Person("Charlie", "否"));
//
//        // 使用Stream API排序
//        people1.sort(Comparator.comparing((Person p) -> StringUtils.isEmpty(p.getName()) ? 1 : 0).thenComparing(p -> p.getJk().equals("否") ? 1 : 0));

    }

    public static void streamDistinct() {

        List<Person> personList = new ArrayList<>();
        personList.add(new Person("Tom", 8900, 23, "male", "New York"));
        personList.add(new Person("Tom", 7000, 25, "male", "Washington"));
        personList.add(new Person("Lily", 7800, 21, "female", "Washington"));
        personList.add(new Person("Lily", 8200, 24, "female", "New York"));
        personList.add(new Person("Owen", 9500, 25, "male", "New York"));
        personList.add(new Person("Owen", 7900, 26, "female", "New York"));

        List<Person> list = personList.stream().collect(Collectors.collectingAndThen(Collectors.toCollection(
            () -> new TreeSet<>(Comparator.comparing(person -> person.getName()+"_"+person.getSex()))), ArrayList::new));

        List<String> nameList = list.stream().map(Person::getName).collect(toList());

        List<String> nameList1 = personList.stream().map(n -> n.getName()).distinct().collect(toList());
        System.out.println("员工姓名去重后：" +nameList);
        System.out.println("员工姓名去重后：" +nameList1);


    }

    public static void streamCollector() {
//        List<Person> personList = new ArrayList<Person>();
//        personList.add(new Person("Tom", 8900, 23, "male", "New York"));
//        personList.add(new Person("Tom", 7000, 25, "male", "Washington"));
//        personList.add(new Person("Lily", 7800, 21, "female", "Washington"));
//        personList.add(new Person("Anni", 8200, 24, "female", "New York"));
//
//        List<String> list = personList.stream().map(Person::getName).collect(Collectors.toList());
//        System.out.println("员工姓名List集合：" +list);
//        // 员工姓名List集合： [Tom, Tom, Lily, Anni]
//
//        Set<String> set = personList.stream().map(Person::getName).collect(Collectors.toSet());
//        System.out.println("员工姓名Set集合：" +set);
//        // 员工姓名Set集合： [Tom, Anni, Lily]
//
//        Map<String, Person> map = personList.stream().filter(p -> p.getSalary() > 8000)
//                .collect(Collectors.toMap(Person::getName, p -> p));
//        System.out.println("员工信息转Map集合："+map);
//        // 员工信息转Map集合：
//        // {
//        //      Tom=Person(name=Tom, salary=8900, age=23, sex=male, area=New York),
//        //      Anni=Person(name=Anni, salary=8200, age=24, sex=female, area=New York)
//        // }

    }

    public static void streamCount() {
        List<Person> personList = new ArrayList<Person>();
        personList.add(new Person("Tom", 8900, 23, "male", "New York"));
        personList.add(new Person("Jack", 7000, 25, "male", "Washington"));
        personList.add(new Person("Lily", 7800, 21, "female", "Washington"));

        // 求数据数量
        Long count = personList.stream().collect(Collectors.counting());
        System.out.println("员工总数：" + count);
        // 员工总数：3

        long count1 = personList.stream().count();
        System.out.println("==="+count1);

        // 求平均工资
        Double average = personList.stream().collect(Collectors.averagingDouble(Person::getSalary));
        System.out.println("员工平均工资：" + average);
        // 员工平均工资：7900.0

        // 求工资之和
        Integer sum = personList.stream().collect(Collectors.summingInt(Person::getSalary));
        System.out.println("员工工资总和：" + sum);
        // 员工工资总和：23700

        // 一次性统计所有信息
        DoubleSummaryStatistics collect = personList.stream().collect(Collectors.summarizingDouble(Person::getSalary));
        System.out.println("员工工资所有统计：" + collect);
        // 员工工资所有统计：
        // DoubleSummaryStatistics{count=3, sum=23700.000000,min=7000.000000, average=7900.000000, max=8900.000000}


    }

    public static void partitioningByAndGroupingBy() {
//        List<Person> personList = new ArrayList<>();
//        personList.add(new Person("Sherry", 9000, 24, "female", "New York"));
//        personList.add(new Person("Tom", 7900, 22, "male", "Washington"));
//        personList.add(new Person("Jack", 9000, 25, "male", "Washington"));
//        personList.add(new Person("Lily", 8800, 26, "female", "New York"));
//
//        // 将员工按薪资是否高于8000分区
//        Map<Boolean, List<Person>> part = personList.stream().collect(Collectors.partitioningBy(x -> x.getSalary() > 8000));
//        System.out.println("员工按薪资是否大于8000分区情况：" + part.toString());
//        //员工按薪资是否大于8000分区情况：{
//        // false=[Person(name=Tom, salary=7900, age=22, sex=male, area=Washington)],
//        // true=[Person(name=Sherry, salary=9000, age=24, sex=female, area=New York),
//        //       Person(name=Jack, salary=9000, age=25, sex=male, area=Washington),
//        //       Person(name=Lily, salary=8800, age=26, sex=female, area=New York)]}
//
//        // 将员工按性别分组
//        Map<String, List<Person>> group = personList.stream().collect(Collectors.groupingBy(Person::getSex));
//        System.out.println("员工按性别分组情况：" + group.toString());
//        //员工按性别分组情况：{
//        // female=[Person(name=Sherry, salary=9000, age=24, sex=female, area=New York),
//        //         Person(name=Lily, salary=8800, age=26, sex=female, area=New York)],
//        // male=[Person(name=Tom, salary=7900, age=22, sex=male, area=Washington),
//        //       Person(name=Jack, salary=9000, age=25, sex=male, area=Washington)]}
//
//        // 将员工先按性别分组，再按地区分组
//        Map<String, Map<String, List<Person>>> group2 = personList.stream().
//                collect(Collectors.groupingBy(Person::getSex, Collectors.groupingBy(Person::getArea)));
//        System.out.println("员工按性别、地区分组：" + group2.toString());
//        //员工按性别、地区分组：{
//        // female={
//        //      New York=[
//        //          Person(name=Sherry, salary=9000, age=24, sex=female, area=New York),
//        //          Person(name=Lily, salary=8800, age=26, sex=female, area=New York)
//        //          ]
//        //       },
//        // male={
//        //      Washington=[
//        //          Person(name=Tom, salary=7900, age=22, sex=male, area=Washington),
//        //          Person(name=Jack, salary=9000, age=25, sex=male, area=Washington)
//        //          ]
//        //      }
//        // }

    }

    public static void streamJoining() {
        List<Person> personList = new ArrayList<>();
        personList.add(new Person("Tom", 8900, 23, "male", "New York"));
        personList.add(new Person("Jack", 7000, 25, "male", "Washington"));
        personList.add(new Person("Lily", 7800, 21, "female", "Washington"));

        String names = personList.stream().map(p -> p.getName()).collect(Collectors.joining(","));
        System.out.println("所有员工的姓名：" + names);
        // 所有员工的姓名：Tom,Jack,Lily
    }


    private static List<UserInfo> getUserList() {
        List<UserInfo> userList = new ArrayList<>();
        for (int i = 1; i < 6; i++) {
            UserInfo user = new UserInfo();
            user.setId(i);
            user.setName("张三");
            user.setAge(18 + i);
            userList.add(user);
        }
        return userList;
    }

    /**
     * peek map
     */
    public static void peekAndMapTest() {
        //只需要访问获取内部元素，打印
        List<String> stringList1 = Arrays.asList("11", "22", "33");
        stringList1.stream().peek(System.out::print).collect(Collectors.toList());

        List<String> stringList2 = Arrays.asList("11", "22", "33");

        //支持自定义返回值，将字符串转换为数字
        List<Integer> mapResultList = stringList2.stream().map(s -> Integer.valueOf(s + 1)).collect(Collectors.toList());
        System.out.println(mapResultList);

        //可以看到返回值还是List<String>
        List<String> peekResultList = stringList2.stream().peek(s -> Integer.parseInt(s + 1)).collect(Collectors.toList());
        System.out.println(stringList2);

    }

    public static void test(){
        //初始化数组
        List<Integer> list = Arrays.asList(45, 56, 23, 89, 34, 78, 56);
        Map<Boolean, List<Integer>> collect = list.stream()
                //过滤 当前数字*2后大于60的,结果：[45,56,89,34,78,56]
                .filter(i -> i * 2 > 60)
                //默认排序,结果:[34,45,56,56,78,89]
                .sorted()
                //每个值-10，成一个map：结果：{24,35,46,46,68,79}
                .map(i -> i - 10)
                //去重,结果：{24,35,46,68,79}
                .distinct()
                //大于五十的分组，输出一个Map<Boolean,Collection>,结果：{false=[24, 35, 46], true=[68, 79]}
                .collect(Collectors.groupingBy(i -> i >= 50, Collectors.toList()));
        System.out.println(collect);

    }

    public static void test2(){
        List<String> strings = Arrays.asList("How are you", "I have a dog", "Hi boy", "cat dog", "boy");

        List<String> collect =strings
                .stream()
                .map(s -> s.split(" "))
                .flatMap(new Function<String[], Stream<String>>() {
                    @Override
                    public Stream<String> apply(String[] strings) {
                        return Stream.of(strings);
                    }
                })
                .distinct()
                .sorted()
                .collect(toList());

        System.out.println(1);
    }

    public static void test3(){
        ArrayList<Person> people = new ArrayList<>();

        Person person = new Person();
        List<UserInfo> userInfos = new ArrayList<>();
        UserInfo userInfo = new UserInfo();
        UserInfo userInfo1 = new UserInfo();
        userInfos.add(userInfo);
        userInfos.add(userInfo1);
        person.setList(userInfos);
        Person person1 = new Person();
        List<UserInfo> userInfos2 = new ArrayList<>();
        UserInfo userInfo3 = new UserInfo();
        UserInfo userInfo4 = new UserInfo();
        userInfos2.add(userInfo3);
        userInfos2.add(userInfo4);
        person1.setList(userInfos2);

        people.add(person);
        people.add(person1);

        List<UserInfo> collect = people.stream().map(Person::getList).flatMap(Collection::stream).collect(toList());
        System.out.println(collect);

    }


}
